const fs = require('fs-extra');
const path = require('path');
const { createLogger } = require('./logger');
const { getContentType, getCacheControl, getAllFiles } = require('./fileHelper');
const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');
dayjs.extend(utc);
dayjs.extend(timezone);
const CHINA_TZ = 'Asia/Shanghai';
function chinaNow() { return dayjs().tz(CHINA_TZ).format('YYYY-MM-DD HH:mm:ss'); }

// 创建日志记录器
const logger = createLogger('cloud-storage-uploader');

/**
 * 模拟上传到云存储
 */
async function simulateUpload(distPath, deployPath, storageConfig, deployNamespace, deployId) {
  // 确保部署路径中的{app}已被替换
  if (deployPath.includes('{app}')) {
    logger.warn(`部署路径中仍包含{app}占位符: ${deployPath}，这可能是一个错误`);
  }

  // 获取所有文件
  const files = getAllFiles(distPath);
  const totalFiles = files.length;
  let uploadedFiles = 0;
  
  logger.info(`准备上传 ${totalFiles} 个文件到 ${deployPath}`);
  
  for (const file of files) {
    const relativePath = path.relative(distPath, file);
    // 使用正斜杠，避免 Windows 路径问题
    const normalizedPath = relativePath.replace(/\\/g, '/');
    const key = `${deployPath}${normalizedPath}`;
    
    // 模拟上传延迟
    await new Promise(resolve => setTimeout(resolve, 100));
    
    uploadedFiles++;
    const progress = Math.round((uploadedFiles / totalFiles) * 100);
    
    logger.info(`上传文件: ${key} (${uploadedFiles}/${totalFiles})`);
    
    if (deployNamespace) {
      deployNamespace.emit('deployProgress', {
        deployId,
        appName: path.basename(deployPath.replace(/\/$/, '')),
        progress,
        currentFile: normalizedPath,
        uploadedFiles,
        totalFiles,
        timestamp: chinaNow()
      });
    }
  }
  
  logger.info(`所有文件上传完成`);
}

/**
 * 上传到云存储
 * 根据存储配置类型调用对应的云存储SDK
 */
async function uploadToCloudStorage(distPath, deployPath, storageConfig, deployNamespace, deployId) {
  try {
    logger.info(`开始上传到云存储: ${storageConfig.type}, 路径: ${deployPath}`);
    
    // 验证存储配置
    if (!validateStorageConfig(storageConfig)) {
      logger.warn(`存储配置验证失败，使用模拟上传`);
      await simulateUpload(distPath, deployPath, storageConfig, deployNamespace, deployId);
      return;
    }
    
    // 根据存储配置类型选择对应的上传方法
    switch (storageConfig.type.toLowerCase()) {
      case 'cos':
        try {
          await uploadToCOS(distPath, deployPath, storageConfig, deployNamespace, deployId);
        } catch (error) {
          // 检查是否是认证错误
          if (error.code === 'InvalidAccessKeyId' || 
              (error.error && error.error.Code === 'InvalidAccessKeyId') ||
              error.message?.includes('access key') ||
              error.statusCode === 403) {
            
            logger.error(`腾讯云COS认证失败，可能是AccessKey格式无效或权限不足，切换到模拟上传模式`, error);
            
            if (deployNamespace) {
              deployNamespace.emit('deployLog', {
                deployId,
                appName: path.basename(deployPath.replace(/\/$/, '')),
                log: '云存储认证失败，切换到模拟上传模式',
                level: 'warn',
                timestamp: chinaNow()
              });
            }
            
            // 认证失败时使用模拟上传
            await simulateUpload(distPath, deployPath, storageConfig, deployNamespace, deployId);
          } else {
            // 其他错误直接抛出
            throw error;
          }
        }
        break;
      case 'oss':
        await uploadToOSS(distPath, deployPath, storageConfig, deployNamespace, deployId);
        break;
      case 'qiniu':
        await uploadToQiniu(distPath, deployPath, storageConfig, deployNamespace, deployId);
        break;
      default:
        // 默认使用模拟上传
        logger.warn(`未支持的存储类型: ${storageConfig.type}，使用模拟上传`);
        await simulateUpload(distPath, deployPath, storageConfig, deployNamespace, deployId);
    }
    
    logger.info(`云存储上传完成`);
  } catch (error) {
    logger.error(`云存储上传失败:`, error);
    
    // 发送错误日志到前端
    if (deployNamespace) {
      deployNamespace.emit('deployLog', {
        deployId,
        appName: path.basename(deployPath.replace(/\/$/, '')),
        log: `云存储上传失败: ${error.message || '未知错误'}`,
        level: 'error',
        timestamp: chinaNow()
      });
    }
    
    throw error;
  }
}

/**
 * 验证存储配置
 */
function validateStorageConfig(config) {
  if (!config) {
    logger.error('存储配置为空');
    return false;
  }
  
  // 验证基本字段
  if (!config.type) {
    logger.error('存储配置缺少 type 字段');
    return false;
  }
  
  // 根据不同类型验证
  switch (config.type.toLowerCase()) {
    case 'cos':
      // 腾讯云COS配置验证
      if (!config.accessKey || !config.secretKey) {
        logger.error('腾讯云COS配置缺少 accessKey 或 secretKey');
        return false;
      }
      if (!config.bucket) {
        logger.error('腾讯云COS配置缺少 bucket');
        return false;
      }
      if (!config.region) {
        logger.error('腾讯云COS配置缺少 region');
        return false;
      }
      
      // 验证accessKey格式 - 腾讯云通常是AKIDxxx格式
      if (config.accessKey && !config.accessKey.startsWith('AKID')) {
        logger.warn(`腾讯云COS accessKey 格式可能不正确，应以 'AKID' 开头`);
        // 不返回false，只是警告
      }
      break;
      
    case 'oss':
      // 阿里云OSS配置验证
      if (!config.accessKey || !config.secretKey) {
        logger.error('阿里云OSS配置缺少 accessKey 或 secretKey');
        return false;
      }
      if (!config.bucket) {
        logger.error('阿里云OSS配置缺少 bucket');
        return false;
      }
      if (!config.region) {
        logger.error('阿里云OSS配置缺少 region');
        return false;
      }
      break;
      
    case 'qiniu':
      // 七牛云配置验证
      if (!config.accessKey || !config.secretKey) {
        logger.error('七牛云配置缺少 accessKey 或 secretKey');
        return false;
      }
      if (!config.bucket) {
        logger.error('七牛云配置缺少 bucket');
        return false;
      }
      break;
      
    default:
      // 未知类型，返回false
      logger.error(`未知的存储类型: ${config.type}`);
      return false;
  }
  
  return true;
}

/**
 * 上传到腾讯云COS
 */
async function uploadToCOS(distPath, deployPath, storageConfig, deployNamespace, deployId) {
  try {
    // 引入腾讯云 COS SDK
    const COS = require('cos-nodejs-sdk-v5');
    
    // 创建 COS 实例
    const cos = new COS({
      SecretId: storageConfig.accessKey,
      SecretKey: storageConfig.secretKey,
      FileParallelLimit: 5,    // 控制文件上传并发数
      ChunkParallelLimit: 8,   // 控制单个文件下分片上传并发数
      ChunkSize: 1024 * 1024 * 8, // 控制分片大小，单位 B
    });
    
    // 获取存储桶名称和地区
    const Bucket = storageConfig.bucket;
    const Region = storageConfig.region;
    
    // 确保部署路径使用正斜杠
    const normalizedDeployPath = deployPath.replace(/\\/g, '/');
    
    logger.info(`开始上传到腾讯云COS: ${Bucket}/${normalizedDeployPath}`);
    
    // 先尝试验证权限
    try {
      await new Promise((resolve, reject) => {
        cos.getBucket({
          Bucket,
          Region,
          Prefix: normalizedDeployPath,
          MaxKeys: 1
        }, function(err, data) {
          if (err) {
            logger.error('验证COS权限失败:', err);
            reject(err);
            return;
          }
          resolve(data);
        });
      });
      
      logger.info('COS权限验证成功');
    } catch (error) {
      logger.error('COS权限验证失败，可能是认证信息错误或无权限:', error);
      throw error;
    }
    
    // 获取所有文件
    const files = getAllFiles(distPath);
    const totalFiles = files.length;
    let uploadedFiles = 0;
    let failedFiles = 0;
    
    logger.info(`准备上传 ${totalFiles} 个文件到 ${Bucket}/${normalizedDeployPath}`);
    
    // 上传所有文件
    for (const file of files) {
      const relativePath = path.relative(distPath, file);
      // 使用正斜杠，避免 Windows 路径问题
      const normalizedPath = relativePath.replace(/\\/g, '/');
      const key = `${normalizedDeployPath}${normalizedPath}`;
      
      // 确定文件的 Content-Type
      const contentType = getContentType(file);
      
      // 确定缓存控制策略
      const cacheControl = getCacheControl(file, storageConfig.cacheControl || 'default');
      
      try {
        // 上传文件
        await new Promise((resolve, reject) => {
          cos.putObject({
            Bucket,
            Region,
            Key: key,
            Body: fs.createReadStream(file),
            ContentType: contentType,
            CacheControl: cacheControl,
            onProgress: function(progressData) {
              // 可选：处理进度信息
            }
          }, function(err, data) {
            if (err) {
              logger.error(`上传文件失败: ${key}`, err);
              reject(err);
              return;
            }
            resolve(data);
          });
        });
        
        uploadedFiles++;
        const progress = Math.round((uploadedFiles / totalFiles) * 100);
        
        logger.info(`上传文件: ${key} (${uploadedFiles}/${totalFiles})`);
        
        if (deployNamespace) {
          deployNamespace.emit('deployProgress', {
            deployId,
            appName: path.basename(normalizedDeployPath.replace(/\/$/, '')),
            progress,
            currentFile: normalizedPath,
            uploadedFiles,
            totalFiles,
            timestamp: chinaNow()
          });
        }
      } catch (error) {
        failedFiles++;
        logger.error(`上传文件 ${key} 失败:`, error);
        
        // 如果是认证错误，则中断整个上传过程
        if (error.code === 'InvalidAccessKeyId' || 
            (error.error && error.error.Code === 'InvalidAccessKeyId') ||
            error.message?.includes('access key') ||
            error.statusCode === 403) {
          throw error; // 抛出错误，中断上传
        }
        
        // 继续上传其他文件，不中断整个过程
      }
    }
    
    logger.info(`所有文件上传完成，共上传 ${uploadedFiles}/${totalFiles} 个文件，失败 ${failedFiles} 个文件`);
    
    // 如果全部失败，抛出错误
    if (uploadedFiles === 0 && failedFiles > 0) {
      throw new Error(`所有文件上传失败，共 ${failedFiles} 个文件`);
    }
    
  } catch (error) {
    logger.error(`腾讯云COS上传过程中发生错误:`, error);
    throw error;
  }
}

/**
 * 上传到阿里云OSS
 */
async function uploadToOSS(distPath, deployPath, storageConfig, deployNamespace, deployId) {
  // TODO: 实现阿里云OSS上传
  logger.info(`TODO: 实现阿里云OSS上传到 ${storageConfig.bucket}/${deployPath}`);
  
  // 临时使用模拟上传
  await simulateUpload(distPath, deployPath, storageConfig, deployNamespace, deployId);
}

/**
 * 上传到七牛云
 */
async function uploadToQiniu(distPath, deployPath, storageConfig, deployNamespace, deployId) {
  // TODO: 实现七牛云上传
  logger.info(`TODO: 实现七牛云上传到 ${storageConfig.bucket}/${deployPath}`);
  
  // 临时使用模拟上传
  await simulateUpload(distPath, deployPath, storageConfig, deployNamespace, deployId);
}

module.exports = {
  uploadToCloudStorage,
  simulateUpload,
  uploadToCOS,
  uploadToOSS,
  uploadToQiniu
}; 